#ifndef WRAP_SOCKET_H
#define WRAP_SOCKET_H

#include <cstdio>

struct sockaddr;

namespace Wrap {

using socklen_t_ = unsigned;

/// @see https://man7.org/linux/man-pages/man2/accept.2.html
/// @param fd It extracts the first connection request on the queue of pending
/// connections for the listening socket, @c fd, creates a new connected
/// socket, and returns a new file descriptor referring to that socket. The
/// newly created socket is not in the listening state.  The original socket
/// sockfd is unaffected by this call.
/// @param addr The argument @c addr is a pointer to a sockaddr structure. This
/// structure is filled in with the address of the peer socket, as known to the
/// communications layer. The exact format of the address returned @c addr is
/// determined by the socket's address family (see socket(2) and the respective
/// protocol man pages).
/// @param addrLen The @c addrlen argument is a value-result argument: the
/// caller must initialize it to contain the size (in bytes) of the structure
/// pointed to by @c addr; on return it will contain the actual size of the
/// peer address.
/// @return On success, these system calls return a file descriptor for the
/// accepted socket (a nonnegative integer). On error, -1 is returned, errno is
/// set to indicate the error, and @c addrlen is left unchanged.
/// @throw EAGAIN The socket is marked nonblocking and no connections are
/// present to be accepted.  POSIX.1-2001 and POSIX.1-2008 allow either error to
/// be returned for this case, and do not require these constants to have the
/// same value, so a portable application should check for both possibilities.
/// @throw EWOULDBLOCK see EAGAIN
/// @throw EBADF @c fd is not an open file descriptor.
/// @throw ECONNABORTED A connection has been aborted.
/// @throw EFAULT The @c addr argument is not in a writable part of the user
/// address space.
/// @throw EINTR The system call was interrupted by a signal that was caught
/// before a valid connection arrived; see signal(7).
/// @throw EINVAL Socket is not listening for connections, or @c addr_len is
/// invalid (e.g., is negative).
/// @throw EINVAL (accept4()) invalid value in flags.
/// @throw EMFILE The per-process limit on the number of open file descriptors
/// has been reached.
/// @throw ENFILE The system-wide limit on the total number of open files has
/// been reached.
/// @throw ENOBUFS Not enough free memory.  This often means that the memory
/// allocation is limited by the socket buffer limits, not by the system memory.
/// @throw ENOMEM see ENOBUFS
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
/// @throw EOPNOTSUPP The referenced socket is not of type SOCK_STREAM.
/// @throw EPROTO Protocol error.
int accept_(int fd, struct sockaddr *addr, socklen_t_ *addrLen);
int accept4_(int fd, struct sockaddr *addr, socklen_t_ *addrLen, int flags);

/// @see https://man7.org/linux/man-pages/man2/bind.2.html
/// @param addr bind() assigns the address specified by @c addr
/// @param fd to the socket referred to by the file descriptor @c fd.
/// @param len @c len specifies the size, in bytes, of the address structure
/// pointed to by @c addr.
/// @return On success, zero is returned. On error, -1 is returned, and errno
/// is set to indicate the error.
/// @throw EACCES The address is protected, and the user is not the superuser.
/// @throw EADDRINUSE The given address is already in use.
/// @throw EADDRINUSE (Internet domain sockets) The port number was specified as
/// zero in the socket address structure, but, upon attempting to bind to an
/// ephemeral port, it was determined that all port numbers in the ephemeral
/// port range are currently in use.  See the discussion of
/// /proc/sys/net/ipv4/ip_local_port_range ip(7).
/// @throw EBADF @c fd is not a valid file descriptor.
/// @throw EINVAL The socket is already bound to an address.
/// @throw EINVAL @c len is wrong, or @c addr is not a valid address for this
/// socket's domain.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
int bind_(int fd, const struct sockaddr *addr, socklen_t_ len);

/// @see https://man7.org/linux/man-pages/man2/connect.2.html
/// @param fd The connect() system call connects the socket referred to by the
/// file descriptor @c fd
/// @param addr to the address specified by @c addr.
/// @param len The @c len argument specifies the size of @c addr.
/// @return If the connection or binding succeeds, zero is returned. On error,
/// -1 is returned, and errno is set to indicate the error.
/// @throw EACCES For UNIX domain sockets, which are identified by pathname:
/// Write permission is denied on the socket file, or search permission is
/// denied for one of the directories in the path prefix.  (See also
/// path_resolution(7).)
/// @throw EACCES The user tried to connect to a broadcast address without
/// having the socket broadcast flag enabled or the connection request failed
/// because of a local firewall rule.
/// @throw EPERM see EACCES
/// @throw EACCES can also be returned if an SELinux policy denied a connection
/// (for example, if there is a policy saying that an HTTP proxy can only
/// connect to ports associated with HTTP servers, and the proxy tries to
/// connect to a different port).
/// @throw EADDRINUSE Local address is already in use.
/// @throw EADDRNOTAVAIL (Internet domain sockets) The socket referred to by @c
/// fd had not previously been bound to an address and, upon attempting to bind
/// it to an ephemeral port, it was determined that all port numbers in the
/// ephemeral port range are currently in use. See the discussion of
/// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
/// @throw EAFNOSUPPORT The passed address didn't have the correct address
/// family in its sa_family field.
/// @throw EAGAIN For nonblocking UNIX domain sockets, the socket is
/// nonblocking, and the connection cannot be completed immediately.  For other
/// socket families, there are insufficient entries in the routing cache.
/// @throw EALREADY The socket is nonblocking and a previous connection attempt
/// has not yet been completed.
/// @throw EBADF @c fd is not a valid open file descriptor.
/// @throw ECONNREFUSED A connect() on a stream socket found no one listening on
/// the remote address.
/// @throw EFAULT The socket structure address is outside the user's address
/// space.
/// @throw EINPROGRESS The socket is nonblocking and the connection cannot be
/// completed immediately.  (UNIX domain sockets failed with EAGAIN instead.) It
/// is possible to select(2) or poll(2) for completion by selecting the socket
/// for writing.  After select(2) indicates writability, use getsockopt(2) to
/// read the SO_ERROR option at level SOL_SOCKET to determine whether connect()
/// completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one
/// of the usual error codes listed here, explaining the reason for the
/// failure).
/// @throw EINTR  The system call was interrupted by a signal that was caught;
/// see signal(7).
/// @throw EISCONN The socket is already connected.
/// @throw ENETUNREACH Network is unreachable.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
/// @throw EPROTOTYPE The socket type does not support the requested
/// communications protocol.  This error can occur, for example, on an attempt
/// to connect a UNIX domain datagram socket to a stream socket.
/// @throw ETIMEDOUT Timeout while attempting connection.  The server may be too
/// busy to accept new connections.  Note that for IP sockets the timeout may be
/// very long when syncookies are enabled on the server.
int connect_(int fd, const struct sockaddr *addr, socklen_t_ len);

/// @see https://man7.org/linux/man-pages/man2/getpeername.2.html
/// @param fd getpeername() returns the address of the peer connected to the
/// socket @c fd,
/// @param addr in the buffer pointed to by @c addr.
/// @param len The @c len argument should be initialized to indicate the amount
/// of space pointed to by @c addr.
/// @return On success, zero is returned. On error, -1 is returned, and errno
/// is set to indicate the error.
/// @throw EBADF The argument @c fd is not a valid file descriptor.
/// @throw EFAULT The @c addr argument points to memory not in a valid part of
/// the process address space.
/// @throw EINVAL @c len is invalid (e.g., is negative).
/// @throw ENOBUFS Insufficient resources were available in the system to
/// perform the operation.
/// @throw ENOTCONN The socket is not connected.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
int getpeername_(int fd, struct sockaddr *addr, socklen_t_ *len);

/// @see https://man7.org/linux/man-pages/man2/getsockname.2.html
/// @param fd getsockname() returns the current address to which the socket
/// @c fd is bound,
/// @param addr in the buffer pointed to by @c addr.
/// @param len The @c len argument should be initialized to indicate the amount
/// of space (in bytes) pointed to by @c addr.
/// @return On success, zero is returned. On error, -1 is returned, and errno
/// is set to indicate the error.
/// @throw EBADF The argument @c fd is not a valid file descriptor.
/// @throw EFAULT The @c addr argument points to memory not in a valid part of
/// the process address space.
/// @throw EINVAL @c len is invalid (e.g., is negative).
/// @throw ENOBUFS Insufficient resources were available in the system to
/// perform the operation.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
int getsockname_(int fd, struct sockaddr *addr, socklen_t_ *len);

/// @see https://man7.org/linux/man-pages/man2/getsockopt.2.html
/// @return On success, zero is returned for the standard options. On error, -1
/// is returned, and errno is set to indicate the error.
/// @throw EBADF The argument @c fd is not a valid file descriptor.
/// @throw EFAULT The address pointed to by @c optval is not in a valid part of
/// the process address space.  For getsockopt(), this error may also be
/// returned if @c optlen is not in a valid part of the process address space.
/// @throw EINVAL @c optlen invalid in setsockopt().  In some cases this error
/// can also occur for an invalid value in @c optval (e.g., for the
/// IP_ADD_MEMBERSHIP option described in ip(7)).
/// @throw ENOPROTOOPT The option is unknown at the level indicated.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
int getsockopt_(int fd, int level, int optname, void *optval, socklen_t_ *optlen);

/// @see https://man7.org/linux/man-pages/man2/listen.2.html
/// @param fd listen() marks the socket referred to by @c fd as a passive
/// socket, that is, as a socket that will be used to accept incoming
/// connection requests using accept(2).
/// @param n The @c n argument defines the maximum length to which the queue of
/// pending connections for @c fd may grow.
/// @throw EADDRINUSE Another socket is already listening on the same port.
/// @throw EADDRINUSE (Internet domain sockets) The socket referred to by @c fd
/// had not previously been bound to an address and, upon attempting to bind it
/// to an ephemeral port, it was determined that all port numbers in the
/// ephemeral port range are currently in use. See the discussion of
/// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
/// @throw EBADF The argument @c fd is not a valid file descriptor.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
/// @throw EOPNOTSUPP The socket is not of a type that supports the listen()
/// operation.
int listen_(int fd, int n);

/// @see https://man7.org/linux/man-pages/man2/recv.2.html
/// @return These calls return the number of bytes received, or -1 if an error
/// occurred. In the event of an error, errno is set to indicate the error.
/// When a stream socket peer has performed an orderly shutdown, the return
/// value will be 0 (the traditional "end-of-file" return). Datagram sockets in
/// various domains (e.g., the UNIX and Internet domains) permit zero-length
/// datagrams. When such a datagram is received, the return value is 0. The
/// value 0 may also be returned if the requested number of bytes to receive
/// from a stream socket was 0.
/// @throw EAGAIN The socket is marked nonblocking and the receive operation
/// would block, or a receive timeout had been set and the timeout expired
/// before data was received. POSIX.1 allows either error to be returned for
/// this case, and does not require these constants to have the same value, so
/// a portable application should check for both possibilities.
/// @throw EWOULDBLOCK see EAGAIN
/// @throw EBADF The argument @c fd is an invalid file descriptor.
/// @throw ECONNREFUSED A remote host refused to allow the network connection
/// (typically because it is not running the requested service).
/// @throw EFAULT The receive buffer pointer(s) point outside the process's
/// address space.
/// @throw EINTR The receive was interrupted by delivery of a signal before any
/// data was available; see signal(7).
/// @throw EINVAL Invalid argument passed.
/// @throw ENOMEM Could not allocate memory for recvmsg().
/// @throw ENOTCONN The socket is associated with a connection-oriented
/// protocol and has not been connected (see connect(2) and accept(2)).
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
ssize_t recv_(int fd, void *buf, size_t n, int flags);

/// @see https://man7.org/linux/man-pages/man2/send.2.html
/// @return On success, these calls return the number of bytes sent. On error,
/// -1 is returned, and errno is set to indicate the error.
/// @throw EACCES (For UNIX domain sockets, which are identified by pathname)
/// Write permission is denied on the destination socket file, or search
/// permission is denied for one of the directories the path prefix. (See
/// path_resolution(7).)
/// (For UDP sockets) An attempt was made to send to a network/broadcast
/// address as though it was a unicast address.
/// @throw EAGAIN The socket is marked nonblocking and the requested operation
/// would block. POSIX.1-2001 allows either error to be returned for this
/// case, and does not require these constants to have the same value, so a
/// portable application should check for both possibilities.
/// @throw EWOULDBLOCK see EAGAIN
/// @throw EAGAIN (Internet domain datagram sockets) The socket referred to by
/// @c fd had not previously been bound to an address and, upon attempting to
/// bind it to an ephemeral port, it was determined that all port numbers in
/// the ephemeral port range are currently in use. See the discussion of
/// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
/// @throw EALREADY Another Fast Open is in progress.
/// @throw EBADF @c fd is not a valid open file descriptor.
/// @throw ECONNRESET Connection reset by peer.
/// @throw EDESTADDRREQ The socket is not connection-mode, and no peer address
/// is set.
/// @throw EFAULT An invalid user space address was specified for an argument.
/// @throw EINTR A signal occurred before any data was transmitted; see
/// signal(7).
/// @throw EINVAL Invalid argument passed.
/// @throw EISCONN The connection-mode socket was connected already but a
/// recipient was specified. (Now either this error is returned, or the
/// recipient specification is ignored.)
/// @throw EMSGSIZE The socket type requires that message be sent atomically,
/// and the size of the message to be sent made this impossible.
/// @throw ENOBUFS The output queue for a network interface was full. This
/// generally indicates that the interface has stopped sending, but may be
/// caused by transient congestion. (Normally, this does not occur in Linux.
/// Packets are just silently dropped when a device queue overflows.)
/// @throw ENOMEM No memory available.
/// @throw ENOTCONN The socket is not connected, and no target has been given.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
/// @throw EOPNOTSUPP Some bit in the @c flags argument is inappropriate for
/// the socket type.
/// @throw EPIPE The local end has been shut down on a connection oriented
/// socket. In this case, the process will also receive a SIGPIPE unless
/// MSG_NOSIGNAL is set.
ssize_t send_(int fd, const void *buf, size_t n, int flags);

/// @see https://man7.org/linux/man-pages/man2/setsockopt.2.html
/// @return On success, zero is returned for the standard options. On error, -1
/// is returned, and errno is set to indicate the error.
/// @throw EBADF The argument @c fd is not a valid file descriptor.
/// @throw EFAULT The address pointed to by @c optval is not in a valid part of
/// the process address space.  For getsockopt(), this error may also be
/// returned if @c optlen is not in a valid part of the process address space.
/// @throw EINVAL @c optlen invalid in setsockopt().  In some cases this error
/// can also occur for an invalid value in @c optval (e.g., for the
/// IP_ADD_MEMBERSHIP option described in ip(7)).
/// @throw ENOPROTOOPT The option is unknown at the level indicated.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
int setsockopt_(int fd, int level, int optname, const void *optval, socklen_t_ optlen);

/// @see https://man7.org/linux/man-pages/man2/shutdown.2.html
/// @param fd The shutdown() call causes all or part of a full-duplex
/// connection on the socket associated with @c fd to be shut down.
/// @param how If @c how is SHUT_RD, further receptions will be disallowed. If
/// @c how is SHUT_WR, further transmissions will be disallowed. If @c how is
/// SHUT_RDWR, further receptions and transmissions will be disallowed.
/// @return On success, zero is returned. On error, -1 is returned, and errno
/// is set to indicate the error.
/// @throw EBADF @c fd is not a valid file descriptor.
/// @throw EINVAL An invalid value was specified in @c how (but see BUGS).
/// @throw ENOTCONN The specified socket is not connected.
/// @throw ENOTSOCK The file descriptor @c fd does not refer to a socket.
int shutdown_(int fd, int how);

/// @see https://man7.org/linux/man-pages/man2/socket.2.html
/// @param domain The @c domain argument specifies a communication domain; this
/// selects the protocol family which will be used for communication. These
/// families are defined in <sys/socket.h>.
/// @param type The socket has the indicated @c type, which specifies the
/// communication semantics.
/// @param protocol The @c protocol specifies a particular protocol to be used
/// with the socket. Normally only a single protocol exists to support a
/// particular socket type within a given protocol family, in which case
/// @c protocol can be specified as 0. However, it is possible that many
/// protocols may exist, in which case a particular protocol must be specified
/// in this manner. The protocol number to use is specific to the
/// “communication domain” in which communication is to take place; see
/// protocols(5). See getprotoent(3) on how to map protocol name strings to
/// protocol numbers.
/// @return  On success, a file descriptor for the new socket is returned. On
/// error, -1 is returned, and errno is set to indicate the error.
/// @throw EACCES Permission to create a socket of the specified type and/or
/// protocol is denied.
/// @throw EAFNOSUPPORT The implementation does not support the specified
/// address family.
/// @throw EINVAL Unknown protocol, or protocol family not available.
/// @throw EINVAL Invalid flags in @c type.
/// @throw EMFILE The per-process limit on the number of open file descriptors
/// has been reached.
/// @throw ENFILE The system-wide limit on the total number of open files has
/// been reached.
/// @throw ENOBUFS Insufficient memory is available.  The socket cannot be
/// created until sufficient resources are freed.
/// @throw ENOMEM see ENOBUFS
/// @throw EPROTONOSUPPORT The protocol type or the specified protocol is not
/// supported within this domain.
int socket_(int domain, int type, int protocol);

}

#endif